Introduction

This R Markdown notebook is used to document the various aspects of the genotype-phenotype analysis in many subjects with hereditary hearing loss based on mutation in the DFNA9 gene. We have data collected […]

This notebooks is intended to leave a trail of the analyses done en to make it more reprodicible. It now covers the data cleaning, description of the data (group size, how many subjects per group, how many audiograms per subject), plots of the hearing thresholds across age and other descriptors of the data. The next step is to

Load R-packages

library(ggplot2)
library(ggthemr)
library(drc)
Loading required package: MASS
Registered S3 methods overwritten by 'car':
  method                          from
  influence.merMod                lme4
  cooks.distance.influence.merMod lme4
  dfbeta.influence.merMod         lme4
  dfbetas.influence.merMod        lme4

'drc' has been loaded.

Please cite R and 'drc' if used for a publication,
for references type 'citation()' and 'citation('drc')'.


Attaching package: ‘drc’

The following objects are masked from ‘package:stats’:

    gaussian, getInitial
library(sjPlot)
#refugeeswelcome
library("readxl")
library("nlme")
library(lme4)
Loading required package: Matrix

Attaching package: ‘lme4’

The following object is masked from ‘package:nlme’:

    lmList
library(knitr)
library(kableExtra)
library(forcats)
library(tidyr)

Attaching package: ‘tidyr’

The following objects are masked from ‘package:Matrix’:

    expand, pack, unpack
library(dplyr)

Attaching package: ‘dplyr’

The following object is masked from ‘package:kableExtra’:

    group_rows

The following object is masked from ‘package:nlme’:

    collapse

The following object is masked from ‘package:MASS’:

    select

The following objects are masked from ‘package:stats’:

    filter, lag

The following objects are masked from ‘package:base’:

    intersect, setdiff, setequal, union
ggthemr('fresh')

Load data and clean data-frames

Load data from Excel file and select only relevant columns/rows. The first analyses will be based on pure-tone average (PTA). The selected subset dataframe consists of the columns patient id (pid), group, age (Leeftijd), and the PTA (PTA54ADS).

data_raw <- read_excel("../data/raw_data/database_20-04-2020.xlsx")
data_raw$group = factor(data_raw$Domain)
#leave out data with only n=1 dataset per domain/certain unpublished data.
data_subset <-
  subset(data_raw, Smits == 'no' & Domainrec != 1 & group != "Ivd1")
data <-
  subset(data_subset, select = c('pid', 'group', 'Leeftijd', 'PTA54ADS'))

#drop unused levels from a factor in a dataframe, e.g. groups that have no entries anymore.
data <- droplevels(data)
# save processed and cleaned data
save(data,file="../data/processed_data/data_pta_age_group.Rda")

#check for NaNs in PTA and Leeftijd, should be 0.
nrow(data[is.na(data$PTA54ADS) | is.na(data$Leeftijd),])
[1] 0

Group description

In the group of DFNA9 patients we have some for which there is longitudinal data, i.e. multiple audiograms over time/age (Leeftijd). How many subjects are there for each group?

t1 <- data %>%                      # take the data.frame "data"
  filter(!is.na(pid)) %>%     # Using "data", filter out all rows with NAs in aa 
  group_by(group) %>%         # Then, with the filtered data, group it by "group"
  summarise("# subjects" = n_distinct(pid))   # Now summarise with unique elements per group
kable(t1, caption = "Table 1. The number of subjects per group",) %>%
  kable_styling(bootstrap_options = "striped", full_width = F)
Table 1. The number of subjects per group
group # subjects
LCCL 251
vWFA1 12
vWFA2 20

Next, create a table and histogram of number of measurements for each subject id (pid) across the groups

num_meas_per_id <-
  aggregate(PTA54ADS ~ pid , data, function(x)
    length(unique(x)))
t2 <- table(num_meas_per_id$PTA54ADS)

In total there are 283 subjects with 716 measurements; 159 patients with only 1 measurement and 123 patients with 2 or more measurements, see e.g. table 1 or the histogram.

kable(t2,
      caption = "Table 2. The number of subjects that each have n audiograms",
      col.names = c("# audiograms", "# subjects")) %>%
  kable_styling(bootstrap_options = "striped", full_width = F)
Table 2. The number of subjects that each have n audiograms
# audiograms # subjects
1 159
2 47
3 21
4 10
5 13
6 11
7 4
8 7
9 3
10 3
11 3
12 1
15 1

Now make a histogram of the number of audiograms across patients in each of the groups

#number (n) of counts (i.e. audiograms) per subject (pid)
summarytable <- data %>% count(group, pid)

ggplot(data = summarytable, aes(x = n, fill = group)) +
  geom_histogram(binwidth = 1) +
  facet_wrap(~ group) +
  xlab("# of measurements") +
  ylab("Frequency (# patients)")

dev.print(pdf, '../results/histogram_number_meas_pid.pdf')
quartz_off_screen 
                2 

Relation of PTA with age for the different groups; connecting lines show longitudinal data of patients’ PTA over time

ggplot(data, aes(
  x = Leeftijd,
  y = PTA54ADS,
  group = pid,
  color = group
)) +
  geom_point(aes(colour = factor(group))) +
  geom_line(data = data, size = 1, alpha = .3) +
  geom_hline(yintercept = 0, linetype = "dashed") +
  scale_x_continuous(breaks = seq(0, 100, 20)) +
  scale_y_reverse(breaks = seq(-10, 130, 20), limits = c(130, -10)) +
  xlab("Age (years)") +
  ylab("PTA (dB HL)") +
  theme_light()

dev.print(pdf, '../results/pta_age_pid_groups.pdf')
quartz_off_screen 
                2 

Logistic fit of PTA with age

Perform fits to the data; first focus on LCCL domain.

lccl = subset(data, group == "LCCL")
ggplot(lccl,
       aes(x = Leeftijd, y = PTA54ADS),
       group = pid,
       color = group) +
  xlab("Age (years)") +
  ylab("PTA (dB HL)") +
  geom_point(aes(colour = factor(group))) +
  geom_hline(yintercept = 0, linetype = "dashed") +
  scale_x_continuous(breaks = seq(0, 100, 20)) +
  scale_y_reverse(breaks = seq(-10, 130, 20), limits = c(130,-10)) +
  theme_light()

dev.print(pdf, '../results/pta_age_pid_lccl.pdf')
quartz_off_screen 
                2 

Try to fit the data with a linear function, a power-law function and a logistic function:

lin_fit <-
  nls(PTA54ADS ~ a * Leeftijd + b,
      data = lccl,
      start = list(a = 1.5, b = 0))
summary(lin_fit)

Formula: PTA54ADS ~ a * Leeftijd + b

Parameters:
   Estimate Std. Error t value Pr(>|t|)    
a   1.60851    0.05331  30.170   <2e-16 ***
b -28.43929    2.89132  -9.836   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 20.01 on 673 degrees of freedom

Number of iterations to convergence: 1 
Achieved convergence tolerance: 4.526e-10
nls_fit <-
  nls(PTA54ADS ~ a * Leeftijd ^ b,
      data = lccl,
      start = list(a = 0.05, b = 1.5))
summary(nls_fit)

Formula: PTA54ADS ~ a * Leeftijd^b

Parameters:
  Estimate Std. Error t value Pr(>|t|)    
a  0.07298    0.01859   3.926 9.51e-05 ***
b  1.66646    0.06175  26.988  < 2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 19.54 on 673 degrees of freedom

Number of iterations to convergence: 5 
Achieved convergence tolerance: 2.93e-06
startvec <- c(Asym = 120, xmid = 50, scal = 15)
nls_logis <- nls(PTA54ADS ~ SSlogis(Leeftijd, Asym, xmid, scal),
                 data = lccl,
                 start = startvec)
summary(nls_logis)

Formula: PTA54ADS ~ SSlogis(Leeftijd, Asym, xmid, scal)

Parameters:
     Estimate Std. Error t value Pr(>|t|)    
Asym  126.351      9.477   13.33   <2e-16 ***
xmid   56.974      2.619   21.76   <2e-16 ***
scal   15.410      1.371   11.24   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 19.29 on 672 degrees of freedom

Number of iterations to convergence: 4 
Achieved convergence tolerance: 3.603e-06

Compare power-law fit and the logistic functione and display the results

anova(nls_fit, nls_logis)
Analysis of Variance Table

Model 1: PTA54ADS ~ a * Leeftijd^b
Model 2: PTA54ADS ~ SSlogis(Leeftijd, Asym, xmid, scal)
  Res.Df Res.Sum Sq Df Sum Sq F value    Pr(>F)    
1    673     257033                                
2    672     250020  1 7013.2   18.85 1.632e-05 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
newdat = expand.grid(Leeftijd = seq(0, 100, by = 1))
newdat$lin <- predict(lin_fit, newdata = newdat)
newdat$pta_logistic <- predict(nls_logis, newdata = newdat)
newdat$pta_power <- predict(nls_fit, newdata = newdat)
#newdat
ggplot(lccl, aes(x = Leeftijd, y = PTA54ADS)) +
  geom_point(aes(colour = factor(group))) +
  geom_line(data = newdat,
            aes(y = lin),
            size = 1,
            col = 'blue') +
  geom_line(data = newdat, aes(y = pta_logistic), size = 1) +
  geom_line(data = newdat,
            aes(y = pta_power),
            size = 1,
            col = 'red') +
  geom_hline(yintercept = 0, linetype = "dashed") +
  scale_x_continuous(breaks = seq(0, 100, 20)) +
  scale_y_reverse(breaks = seq(-10, 130, 20), limits = c(130, -10)) +
  #scale_y_reverse(limits=c(130,-10)) +
  xlab("Age (years)") +
  ylab("PTA (dB HL)") +
  theme_light()

dev.print(pdf, '../results/pta_age_lccl_fits.pdf')
quartz_off_screen 
                2 

As we can see, the logistic function (SSlogis) describes the data better than the power-law function (F = 18,9; p = 1.6 e-5) This function has also been used in desribing the (frequency-specific) thresholds in Pauw et al., 2011 and will used in the subsequent sections.

Group comparison

The main questions is whether the function that describes the PTA (dB HL) as a function of age (years) differs between the groups @ref(fig:plot_pta_age_groups).

Start with a group-fit; discarding grouping information

fit0 <-
  nls(PTA54ADS ~ SSlogis(Leeftijd, Asym, xmid, scal), data = data)
summary(fit0)

Formula: PTA54ADS ~ SSlogis(Leeftijd, Asym, xmid, scal)

Parameters:
     Estimate Std. Error t value Pr(>|t|)    
Asym  135.557     12.188   11.12   <2e-16 ***
xmid   59.258      3.517   16.85   <2e-16 ***
scal   17.807      1.639   10.86   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 19.78 on 713 degrees of freedom

Number of iterations to convergence: 4 
Achieved convergence tolerance: 2.948e-06
coef(fit0)
     Asym      xmid      scal 
135.55740  59.25776  17.80669 

Now, add a grouping-variable with the mid-point (xmid)

# https://stats.stackexchange.com/questions/27273/how-do-i-fit-a-nonlinear-mixed-effects-model-for-repeated-measures-data-using-nl
# https://stats.stackexchange.com/questions/316801/how-to-compare-logistic-regression-curves
fit1 <- nls(
  PTA54ADS ~ SSlogis(Leeftijd, Asym, xmid[group], scal),
  data = data,
  start = list(
    Asym = rep(120, 1),
    xmid = rep(50, 3),
    scal = rep(15, 1)
  )
)
summary(fit1)

Formula: PTA54ADS ~ SSlogis(Leeftijd, Asym, xmid[group], scal)

Parameters:
      Estimate Std. Error t value Pr(>|t|)    
Asym   116.114      6.242   18.60   <2e-16 ***
xmid1   54.188      1.813   29.89   <2e-16 ***
xmid2   43.644      3.462   12.61   <2e-16 ***
xmid3   34.631      3.185   10.87   <2e-16 ***
scal    14.394      1.127   12.77   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 19.13 on 711 degrees of freedom

Number of iterations to convergence: 7 
Achieved convergence tolerance: 4.654e-06

And add the scaling [scal] as a grouping variable; does it futher explain differences between groups?

fit2 <-
  nls(
    PTA54ADS ~ SSlogis(Leeftijd, Asym, xmid[group], scal[group]),
    data = data,
    start = list(
      Asym = rep(120, 1),
      xmid = rep(50, 3),
      scal = rep(15, 3)
    )
  )
summary(fit2)

Formula: PTA54ADS ~ SSlogis(Leeftijd, Asym, xmid[group], scal[group])

Parameters:
      Estimate Std. Error t value Pr(>|t|)    
Asym   123.891      8.563  14.468  < 2e-16 ***
xmid1   56.304      2.381  23.652  < 2e-16 ***
xmid2   49.833      6.541   7.618 8.24e-14 ***
xmid3   37.568      5.595   6.715 3.86e-11 ***
scal1   15.084      1.291  11.682  < 2e-16 ***
scal2   24.884      8.419   2.956  0.00322 ** 
scal3   26.860      6.824   3.936 9.09e-05 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 19.05 on 709 degrees of freedom

Number of iterations to convergence: 6 
Achieved convergence tolerance: 2.195e-06
plot(profile(fit2))

And now add the asymptotic value of the fit (Leeftijd -> infinity) (Asym):

fit3 <-
  nls(
    PTA54ADS ~ SSlogis(Leeftijd, Asym[group], xmid[group], scal[group]),
    data = data,
    start = list(
      Asym = rep(120, 3),
      xmid = rep(50, 3),
      scal = rep(15, 3)
    )
  )
summary(fit3)

Formula: PTA54ADS ~ SSlogis(Leeftijd, Asym[group], xmid[group], scal[group])

Parameters:
      Estimate Std. Error t value Pr(>|t|)    
Asym1  126.350      9.361  13.497  < 2e-16 ***
Asym2   89.933     60.449   1.488   0.1373    
Asym3   97.280     10.751   9.049  < 2e-16 ***
xmid1   56.974      2.587  22.025  < 2e-16 ***
xmid2   34.989     26.673   1.312   0.1900    
xmid3   27.148      4.666   5.819 8.99e-09 ***
scal1   15.410      1.354  11.383  < 2e-16 ***
scal2   17.424     17.929   0.972   0.3315    
scal3   13.988      5.807   2.409   0.0163 *  
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 19.05 on 707 degrees of freedom

Number of iterations to convergence: 8 
Achieved convergence tolerance: 3.258e-06

Now test the various models. Which of the parameters explain the data best?

anova(fit0, fit1, fit2, fit3)
Analysis of Variance Table

Model 1: PTA54ADS ~ SSlogis(Leeftijd, Asym, xmid, scal)
Model 2: PTA54ADS ~ SSlogis(Leeftijd, Asym, xmid[group], scal)
Model 3: PTA54ADS ~ SSlogis(Leeftijd, Asym, xmid[group], scal[group])
Model 4: PTA54ADS ~ SSlogis(Leeftijd, Asym[group], xmid[group], scal[group])
  Res.Df Res.Sum Sq Df  Sum Sq F value    Pr(>F)    
1    713     279067                                 
2    711     260324  2 18743.2 25.5958 1.844e-11 ***
3    709     257309  2  3014.8  4.1535   0.01609 *  
4    707     256670  2   639.6  0.8809   0.41487    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

It turns out the both the variables [xmid] and [scale], i.e. the midpoint and slope at the midpoint significantly differ between the three groups, but that adding the asymptotic value does not describe the data signigicantly better (F=0.89, p=0.41). Fit the data and plot the results:

newdat = expand.grid(Leeftijd = seq(0, 100, by = 1),
                     group = c("LCCL", "vWFA1", "vWFA2"))
newdat$fit <- predict(fit2, newdata = newdat)
ggplot(data, aes(
  x = Leeftijd,
  y = PTA54ADS,
  group = pid,
  color = group
)) +
  geom_point(aes(colour = factor(group)), alpha = .4) +
  geom_line(data = data, size = 1, alpha = .2) +
  geom_line(data = newdat,
            aes(
              y = fit,
              group = group,
              colour = factor(group)
            ),
            size = 1) +
  geom_hline(yintercept = 0, linetype = "dashed") +
  scale_x_continuous(breaks = seq(0, 100, 20)) +
  scale_y_reverse(breaks = seq(-10, 130, 20), limits = c(130, -10)) +
  xlab("Age (years)") +
  ylab("PTA (dB HL)") +
  theme_light()

dev.print(pdf, '../results/pta_age_pid_groups_fits.pdf')
quartz_off_screen 
                2 

Now perform fit on individual data by subsetting the data to keep individuals with more that x=2 longitudinal datapoints. Is it the case that using a non-linear mixed-model approach may help us?

#https://stackoverflow.com/questions/14439770/filter-rows-in-dataframe-by-number-of-rows-per-level-of-a-factor
pidlengths <- ave(as.numeric(data$pid),
                  data$pid, FUN = length)
#df2 <- lccl[pidlengths > 5, ]
df2 <- data[pidlengths > 2,]
with(df2, table(group))
group
 LCCL vWFA1 vWFA2 
  459     3     6 

So, with only two data-points, only 3 and 6 subjects for the vWFA1 and vWFA2 domain respectively, remain. Now, fit those with a logistic function (SSlogis) using nlslist.

models <-
  nlsList(PTA54ADS ~ SSlogis(Leeftijd, Asym, xmid, scal) |
            pid, data = df2)
37 errors caught in (attr(object, "initial"))(mCall = mCall, data = data, LHS = LHS).  The error messages and their frequencies are

                                    singular matrix 'a' in solve 
                                                               1 
                                               singular gradient 
                                                               2 
step factor 0.000488281 reduced below 'minFactor' of 0.000976562 
                                                              12 
           too few distinct input values to fit a logistic model 
                                                              22 

As we can see, some model-predictions failed; they end up with NaNs in the model fit list (nlslist); see e.g. pid 147

data_id <- subset(df2, pid == "147")
data_id
ggplot(data = data_id,  aes(x = Leeftijd, y = PTA54ADS)) +
  geom_point() +
  geom_hline(yintercept = 0, linetype = "dashed") +
  scale_x_continuous(breaks = seq(0, 100, 20)) +
  scale_y_reverse(breaks = seq(-10, 130, 20), limits = c(130, -10)) +
  #scale_y_reverse(limits=c(130,-10)) +
  xlab("Age (years)") +
  ylab("PTA (dB HL)") +
  xlim(0,100)+
  theme_light() 
Scale for 'x' is already present. Adding another scale for 'x', which will replace the existing scale.

Predict for all pid’s the fit to the model and remove the pid’s that give NaNs. Check how many subjects per group we end up with.

df2$Pred <- predict(models)
df2_na <- na.omit(df2)
df2_na_stats <- with(df2_na, table(group, pid))
df2_na_stats
       pid
group   17 18 19 23 24 25 26 27 28 29 43 45 69 99 113 116 117 146 154 160 163 165 174 175 182 186 187 202
  LCCL   6  6  6  8  6  6  9  6  9  5  4  8  7  7   5   5   7  10  10   5   8   4   7   8   8   5   6   5
  vWFA1  0  0  0  0  0  0  0  0  0  0  0  0  0  0   0   0   0   0   0   0   0   0   0   0   0   0   0   0
  vWFA2  0  0  0  0  0  0  0  0  0  0  0  0  0  0   0   0   0   0   0   0   0   0   0   0   0   0   0   0
       pid
group   203 207 209 215 218 220 222 223 225 230 234 251 255 267 268
  LCCL    5   5   4   8   6  11  10  11   9  14  10   8   7   8   5
  vWFA1   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0
  vWFA2   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0

We only keep the pid’s from the LCCL group. The pid’s in the other groups are not properly fitted. Also note that the minimum of data-points for a reasonable fit is 4.

le <- unique(df2_na$pid)
newdat = expand.grid(Leeftijd = seq(0, 100, by = 1), pid = le)
newdat$prednlm <- predict(models, newdata = newdat)
#https://stackoverflow.com/questions/37122994/plotting-a-list-of-non-linear-regressions-with-ggplot
#https://aosmith.rbind.io/2018/11/16/plot-fitted-lines/
ggplot(data = df2_na,  aes(x = Leeftijd, y = PTA54ADS, colour = pid)) +
  geom_point() +
  geom_line(data = newdat, aes(y = prednlm)) +
  facet_wrap(~ pid) +
  geom_hline(yintercept = 0, linetype = "dashed") +
  scale_x_continuous(breaks = seq(0, 100, 25)) +
  scale_y_reverse(breaks = seq(0, 120, 40), limits = c(130,-10)) +
  #scale_y_reverse(limits=c(130,-10)) +
  xlab("Age (years)") +
  ylab("PTA (dB HL)") +
  theme_light()


dev.print(pdf, '../results/pta_age_pid_lccl_ind_fits.pdf')
quartz_off_screen 
                2 

So, it seeems we can fit the data for individual subjects by some extent. It often ‘fails’ by over- or underestimating the tail (coef.lmlist Asym column). We can also plot it all in one figure.

ggplot(data = df2_na,  aes(x = Leeftijd, y = PTA54ADS, colour = pid)) +
  geom_point() +
  geom_line(data = newdat, aes(y = prednlm, group = pid)) +
  geom_hline(yintercept = 0, linetype = "dashed") +
  scale_x_continuous(breaks = seq(0, 100, 20)) +
  scale_y_reverse(breaks = seq(-10, 130, 20), limits = c(130,-10)) +
  xlab("Age (years)") +
  ylab("PTA (dB HL)") +
  theme_light()

dev.print(pdf, '../results/pta_age_pid_lccl_ind_fits_overlay.pdf')
quartz_off_screen 
                2 

Feed the remaining data into the non-linear mixed-models with the parameters Asym, xmid, and scal as random factors.

nm1 <-
  nlmer(
    PTA54ADS ~ SSlogis(Leeftijd, Asym, xmid, scal) ~ Asym + xmid + scal |
      pid,
    df2_na,
    method="ML",
    start = c(Asym = 100, xmid = 60, scal = 15),
    corr = FALSE
  )
summary(nm1)
variance-covariance matrix computed from finite-difference Hessian is
not positive definite or contains NA values: falling back to var-cov estimated from RXvariance-covariance matrix computed from finite-difference Hessian is
not positive definite or contains NA values: falling back to var-cov estimated from RX
Nonlinear mixed model fit by maximum likelihood  ['nlmerMod']
Formula: PTA54ADS ~ SSlogis(Leeftijd, Asym, xmid, scal) ~ Asym + xmid +      scal | pid
   Data: df2_na

     AIC      BIC   logLik deviance df.resid 
  2152.6   2189.9  -1066.3   2132.6      297 

Scaled residuals: 
     Min       1Q   Median       3Q      Max 
-3.14923 -0.50051 -0.03471  0.44682  2.94717 

Random effects:
 Groups   Name Variance Std.Dev. Corr       
 pid      Asym 166.742  12.913              
          xmid  44.562   6.675    0.23      
          scal   4.541   2.131   -0.53  0.11
 Residual       24.231   4.922              
Number of obs: 307, groups:  pid, 43

Fixed effects:
     Estimate Std. Error t value
Asym 113.0219     3.6536   30.93
xmid  52.5762     1.1214   46.88
scal   8.4703     0.5342   15.86

Correlation of Fixed Effects:
     Asym  xmid 
xmid 0.409      
scal 0.343 0.265
plot(ranef(nm1,augFrame=T))
additional arguments to ranef.merMod ignored: augFrame
$pid

params <- coef(nm1)
head(params)
$pid
sjplot(nm1)
Error: `data` must be a data frame.
require(lattice)
qqmath(ranef(nm1, condVar = TRUE))

Frequencey-specific analyses

Now subset the data to contain individual frequencies

data_all <-
  subset(
    data_subset,
    select = c(
      'pid',
      'group',
      'Leeftijd',
      '250.AD',
      '500.AD',
      '1000.AD',
      '2000.AD',
      '4000.AD',
      '8000.AD',
      '250.AS',
      '500.AS',
      '1000.AS',
      '2000.AS',
      '4000.AS',
      '8000.AS'
    )
  )
head(data_all)

Convert ‘wide’ dataset into ‘long’ format using tidyr and remove NaNs

tidier <- data_all %>%
  gather(f, dB,-pid,-group,-Leeftijd)
data_all_l <- tidier %>%
  separate(f, into = c("frequency", "ear"), sep = "\\.")
#head(data_all_l)
data_all_l$frequency = factor(data_all_l$frequency)
data_all_l$ear = factor(data_all_l$ear)
data_all_l <- na.omit(data_all_l)
head(data_all_l)
p <- data_all_l %>%
  mutate(frequency = fct_relevel(frequency, "250", "500", "1000", "2000", "4000", "8000")) %>%
  ggplot(aes(x = frequency, y = dB)) +
  #geom_bar(stat="identity") +
  #geom_histogram() +
  geom_violin() +
  facet_wrap( ~ group, ncol = 3) +
  #geom_point()
  xlab("Frequency (Hz)") +
  ylab("Hearing level (dB)") +
  scale_y_reverse(breaks = seq(-10, 130, 20), limits = c(130, -10)) +
  theme_light()
  #theme_classic()
p

dev.print(pdf, '../results/violin_plot_HL_groups.pdf')
quartz_off_screen 
                2 
data_all_l$f = factor(data_all_l$frequency,
                      levels = c('250', '500', '1000', '2000', '4000', '8000'))
ggplot(data_all_l, aes(
  x = Leeftijd,
  y = dB,
  group = f,
  color = group
)) +
  geom_point(size = 0.7) +
  facet_wrap( ~ f) +
  geom_hline(yintercept = 0, linetype = "dashed") +
  scale_x_continuous(breaks = seq(0, 100, 25)) +
  scale_y_reverse(breaks = seq(0, 120, 40), limits = c(130, -10)) +
  #scale_y_reverse(limits=c(130,-10)) +
  xlab("Age (years)") +
  ylab("Hearing level (dB HL)") +
  theme_light()

dev.print(pdf, '../results/HL_age_frequency_groups.pdf')
quartz_off_screen 
                2 
models <-
  nlsList(
    dB ~ SSlogis(Leeftijd, Asym, xmid, scal) |
      f,
    data = data_all_l,
    start = c(Asym = 100, xmid = 60, scal = 15)
  )
summary(models)
Call:
  Model: dB ~ SSlogis(Leeftijd, Asym, xmid, scal) | f 
   Data: data_all_l 

Coefficients:
   Asym 
     Estimate Std. Error   t value     Pr(>|t|)
250  143.7417  15.611184  9.207613 1.162671e-24
500  136.8704  11.267641 12.147213 1.477519e-37
1000 133.4395   9.988906 13.358770 7.508358e-38
2000 127.9221   8.412012 15.207072 1.571244e-46
4000 128.0742   5.506317 23.259510 4.517456e-97
8000 136.8990   6.104713 22.425128 2.187622e-77
   xmid 
     Estimate Std. Error  t value      Pr(>|t|)
250  68.56354   3.950056 17.35761  2.625853e-76
500  64.58276   2.837787 22.75814 2.394055e-112
1000 62.46279   2.596693 24.05475 5.901932e-106
2000 56.71821   2.511112 22.58689  5.467414e-93
4000 46.43124   1.686311 27.53420 3.096978e-128
8000 44.43887   1.880505 23.63135  1.241122e-84
   scal 
     Estimate Std. Error  t value     Pr(>|t|)
250  17.45580   1.382288 12.62819 1.537972e-43
500  15.90778   1.135068 14.01482 1.494674e-48
1000 15.75834   1.114869 14.13470 6.735219e-42
2000 17.06383   1.281637 13.31409 1.214153e-36
4000 17.16685   1.229298 13.96475 1.180268e-39
8000 18.78508   1.406201 13.35874 6.454529e-31

Residual standard error: 22.81709 on 8329 degrees of freedom
ggplot(data = data_all_l, aes(
  x = Leeftijd,
  y = dB,
  group = group,
  color = group
)) +
  #geom_point(size=0.4) +
  geom_jitter(size = 0.4) +
  geom_smooth(method = "lm") +
  facet_wrap( ~ f) +
  xlab("Age (years)") +
  ylab("PTA (dB HL)") +
  geom_hline(yintercept = 0, linetype = "dashed") +
  scale_x_continuous(breaks = seq(0, 100, 20)) +
  scale_y_reverse(breaks = seq(-10, 130, 20), limits = c(130, -10)) +
  theme_light()

Now use the linear fits to construct an ARTA

par(pty = "s")
#see: https://stackoverflow.com/questions/1169539/linear-regression-and-group-by-in-r
library(lme4)
arta_data <- subset(data_all_l, group == "vWFA2")
fits.plm <- lmList(dB ~ Leeftijd | frequency, data = arta_data)
Unknown or uninitialised column: `(weights)`.Unknown or uninitialised column: `(offset)`.Unknown or uninitialised column: `(weights)`.Unknown or uninitialised column: `(offset)`.Unknown or uninitialised column: `(weights)`.Unknown or uninitialised column: `(offset)`.Unknown or uninitialised column: `(weights)`.Unknown or uninitialised column: `(offset)`.Unknown or uninitialised column: `(weights)`.Unknown or uninitialised column: `(offset)`.Unknown or uninitialised column: `(weights)`.Unknown or uninitialised column: `(offset)`.
coef(fits.plm)
ci <- confint(fits.plm)
plot(ci)

newdat = expand.grid(
  Leeftijd = seq(20, 70, by = 10),
  frequency = c("250", "500", "1000", "2000", "4000", "8000")
)
newdat$fit <- predict(fits.plm, newdata = newdat)

head(newdat)

ggplot(data = newdat, aes(x = frequency, y = fit, group = Leeftijd)) +
  geom_line(aes(
    x = frequency,
    y = fit,
    group = Leeftijd,
    color = Leeftijd
  )) +
  scale_y_reverse(breaks = seq(-10, 130, 20), limits = c(130, -10)) +
  # scale_x_discrete(breaks=c("250","500","1000","2000","4000","8000"), labels=c("0.25","0.5","1","2","4","8"")) +
  xlab("Frequency (Hz)") +
  ylab("Hearing threshold (dB HL)") +
  guides(color = guide_legend("Leeftijd")) +
  theme_classic()

library(dplyr)
fitted_models = data_all_l %>% group_by(frequency) %>% do(model = lm(dB ~ Leeftijd, data = .))
fitted_models

simple linear model: PTA is a function of the affected domain; here are two levels in this mixed model; 1: timepoints for each patient; 2: genetic domain, with domain the fixed effect and patient the random effect allowing the intercept to vary across patient (~1|pid).

random_intercept <- lme(
  dB ~ frequency ,
  random = ~ 1 | pid,
  #p. 896
  method = "ML",
  na.action = na.exclude,
  control = list(opt = "optim"),
  correlation = corAR1(),
  #see p.897; timepoints are not equally spaced;use corCAR1
  data = data_all_l
)
summary(random_intercept)
anova(random_intercept)

now add Leeftijd as fixed effect; PTA ~ Domain + Leeftijd` (see. e.g. p.897)

timeRI <- update(random_intercept, . ~ . + Leeftijd)
summary(timeRI)
sessionInfo()

Code Appendix

LS0tCnRpdGxlOiAiREZOQTkgZ2Vub3R5cGUgfiBwaGVub3R5cGUgYW5hbHlzaXMiCmF1dGhvcjogIkNyaXMgTGFudGluZyIKZGF0ZTogIjIyLzA0LzIwMjAiCm91dHB1dDoKICBwZGZfZG9jdW1lbnQ6IGRlZmF1bHQKICBmaWdfd2lkdGg6IDYKICBmaWdfaGVpZ2h0OiA0CiAgaHRtbF9kb2N1bWVudDoKICAgIGRmX3ByaW50OiBwYWdlZAogIHRvYzogeWVzCiAgdG9jX2Zsb2F0OiB5ZXMKLS0tCiMjIEludHJvZHVjdGlvbgpUaGlzIFtSIE1hcmtkb3duXShodHRwOi8vcm1hcmtkb3duLnJzdHVkaW8uY29tKSBub3RlYm9vayBpcyB1c2VkIHRvIGRvY3VtZW50IHRoZSB2YXJpb3VzIGFzcGVjdHMgb2YgdGhlIGdlbm90eXBlLXBoZW5vdHlwZSBhbmFseXNpcyBpbiBtYW55IHN1YmplY3RzIHdpdGggaGVyZWRpdGFyeSBoZWFyaW5nIGxvc3MgYmFzZWQgb24gbXV0YXRpb24gaW4gdGhlIERGTkE5IGdlbmUuIFdlIGhhdmUgZGF0YSBjb2xsZWN0ZWQgWy4uLl0KClRoaXMgbm90ZWJvb2tzIGlzIGludGVuZGVkIHRvIGxlYXZlIGEgdHJhaWwgb2YgdGhlIGFuYWx5c2VzIGRvbmUgZW4gdG8gbWFrZSBpdCBtb3JlIHJlcHJvZGljaWJsZS4gSXQgbm93IGNvdmVycyB0aGUgZGF0YSBjbGVhbmluZywgZGVzY3JpcHRpb24gb2YgdGhlIGRhdGEgKGdyb3VwIHNpemUsIGhvdyBtYW55IHN1YmplY3RzIHBlciBncm91cCwgaG93IG1hbnkgYXVkaW9ncmFtcyBwZXIgc3ViamVjdCksIHBsb3RzIG9mIHRoZSBoZWFyaW5nIHRocmVzaG9sZHMgYWNyb3NzIGFnZSBhbmQgb3RoZXIgZGVzY3JpcHRvcnMgb2YgdGhlIGRhdGEuIFRoZSBuZXh0IHN0ZXAgaXMgdG8gCgojIyBMb2FkIFItcGFja2FnZXMKYGBge3J9CmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShnZ3RoZW1yKQpsaWJyYXJ5KGRyYykKbGlicmFyeShzalBsb3QpCmxpYnJhcnkoInJlYWR4bCIpCmxpYnJhcnkoIm5sbWUiKQpsaWJyYXJ5KGxtZTQpCmxpYnJhcnkoa25pdHIpCmxpYnJhcnkoa2FibGVFeHRyYSkKbGlicmFyeShmb3JjYXRzKQpsaWJyYXJ5KHRpZHlyKQpsaWJyYXJ5KGRwbHlyKQpgYGAKYGBge3J9CmdndGhlbXIoJ2ZyZXNoJykKYGBgCgojIyBMb2FkIGRhdGEgYW5kIGNsZWFuIGRhdGEtZnJhbWVzCkxvYWQgZGF0YSBmcm9tIEV4Y2VsIGZpbGUgYW5kIHNlbGVjdCBvbmx5IHJlbGV2YW50IGNvbHVtbnMvcm93cy4gVGhlIGZpcnN0IGFuYWx5c2VzIHdpbGwgYmUgYmFzZWQgb24gcHVyZS10b25lIGF2ZXJhZ2UgKFBUQSkuIFRoZSBzZWxlY3RlZCBzdWJzZXQgZGF0YWZyYW1lIGNvbnNpc3RzIG9mIHRoZSBjb2x1bW5zIHBhdGllbnQgaWQgKHBpZCksIGdyb3VwLCBhZ2UgKExlZWZ0aWpkKSwgYW5kIHRoZSBQVEEgKFBUQTU0QURTKS4KCmBgYHtyfQpkYXRhX3JhdyA8LSByZWFkX2V4Y2VsKCIuLi9kYXRhL3Jhd19kYXRhL2RhdGFiYXNlXzIwLTA0LTIwMjAueGxzeCIpCmRhdGFfcmF3JGdyb3VwID0gZmFjdG9yKGRhdGFfcmF3JERvbWFpbikKI2xlYXZlIG91dCBkYXRhIHdpdGggb25seSBuPTEgZGF0YXNldCBwZXIgZG9tYWluL2NlcnRhaW4gdW5wdWJsaXNoZWQgZGF0YS4KZGF0YV9zdWJzZXQgPC0KICBzdWJzZXQoZGF0YV9yYXcsIFNtaXRzID09ICdubycgJiBEb21haW5yZWMgIT0gMSAmIGdyb3VwICE9ICJJdmQxIikKZGF0YSA8LQogIHN1YnNldChkYXRhX3N1YnNldCwgc2VsZWN0ID0gYygncGlkJywgJ2dyb3VwJywgJ0xlZWZ0aWpkJywgJ1BUQTU0QURTJykpCgojZHJvcCB1bnVzZWQgbGV2ZWxzIGZyb20gYSBmYWN0b3IgaW4gYSBkYXRhZnJhbWUsIGUuZy4gZ3JvdXBzIHRoYXQgaGF2ZSBubyBlbnRyaWVzIGFueW1vcmUuCmRhdGEgPC0gZHJvcGxldmVscyhkYXRhKQojIHNhdmUgcHJvY2Vzc2VkIGFuZCBjbGVhbmVkIGRhdGEKc2F2ZShkYXRhLGZpbGU9Ii4uL2RhdGEvcHJvY2Vzc2VkX2RhdGEvZGF0YV9wdGFfYWdlX2dyb3VwLlJkYSIpCgojY2hlY2sgZm9yIE5hTnMgaW4gUFRBIGFuZCBMZWVmdGlqZCwgc2hvdWxkIGJlIDAuCm5yb3coZGF0YVtpcy5uYShkYXRhJFBUQTU0QURTKSB8IGlzLm5hKGRhdGEkTGVlZnRpamQpLF0pCgpgYGAKIyMgR3JvdXAgZGVzY3JpcHRpb24KCgpJbiB0aGUgZ3JvdXAgb2YgREZOQTkgcGF0aWVudHMgd2UgaGF2ZSBzb21lIGZvciB3aGljaCB0aGVyZSBpcyBsb25naXR1ZGluYWwgZGF0YSwgaS5lLiBtdWx0aXBsZSBhdWRpb2dyYW1zIG92ZXIgdGltZS9hZ2UgKExlZWZ0aWpkKS4gSG93IG1hbnkgc3ViamVjdHMgYXJlIHRoZXJlIGZvciBlYWNoIGdyb3VwPwoKYGBge3J9CnQxIDwtIGRhdGEgJT4lICAgICAgICAgICAgICAgICAgICAgICMgdGFrZSB0aGUgZGF0YS5mcmFtZSAiZGF0YSIKICBmaWx0ZXIoIWlzLm5hKHBpZCkpICU+JSAgICAgIyBVc2luZyAiZGF0YSIsIGZpbHRlciBvdXQgYWxsIHJvd3Mgd2l0aCBOQXMgaW4gYWEgCiAgZ3JvdXBfYnkoZ3JvdXApICU+JSAgICAgICAgICMgVGhlbiwgd2l0aCB0aGUgZmlsdGVyZWQgZGF0YSwgZ3JvdXAgaXQgYnkgImdyb3VwIgogIHN1bW1hcmlzZSgiIyBzdWJqZWN0cyIgPSBuX2Rpc3RpbmN0KHBpZCkpICAgIyBOb3cgc3VtbWFyaXNlIHdpdGggdW5pcXVlIGVsZW1lbnRzIHBlciBncm91cAprYWJsZSh0MSwgY2FwdGlvbiA9ICJUYWJsZSAxLiBUaGUgbnVtYmVyIG9mIHN1YmplY3RzIHBlciBncm91cCIsKSAlPiUKICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gInN0cmlwZWQiLCBmdWxsX3dpZHRoID0gRikKYGBgCgpOZXh0LCBjcmVhdGUgYSB0YWJsZSBhbmQgaGlzdG9ncmFtIG9mIG51bWJlciBvZiBtZWFzdXJlbWVudHMgZm9yIGVhY2ggc3ViamVjdCBpZCAocGlkKSBhY3Jvc3MgdGhlIGdyb3VwcwoKYGBge3J9Cm51bV9tZWFzX3Blcl9pZCA8LQogIGFnZ3JlZ2F0ZShQVEE1NEFEUyB+IHBpZCAsIGRhdGEsIGZ1bmN0aW9uKHgpCiAgICBsZW5ndGgodW5pcXVlKHgpKSkKdDIgPC0gdGFibGUobnVtX21lYXNfcGVyX2lkJFBUQTU0QURTKQpgYGAKCkluIHRvdGFsIHRoZXJlIGFyZSBgciBzdW0odDIpYCBzdWJqZWN0cyB3aXRoIGByIGRpbShkYXRhKVsxXWAgIG1lYXN1cmVtZW50czsgYHIgdDJbMV1gIHBhdGllbnRzIHdpdGggb25seSAxIG1lYXN1cmVtZW50IGFuZCBgciBzdW0odDJbMjoxMl0pYCBwYXRpZW50cyB3aXRoIDIgb3IgbW9yZSBtZWFzdXJlbWVudHMsIHNlZSBlLmcuIHRhYmxlIDEgb3IgdGhlIGhpc3RvZ3JhbS4KCmBgYHtyfQprYWJsZSh0MiwKICAgICAgY2FwdGlvbiA9ICJUYWJsZSAyLiBUaGUgbnVtYmVyIG9mIHN1YmplY3RzIHRoYXQgZWFjaCBoYXZlIG4gYXVkaW9ncmFtcyIsCiAgICAgIGNvbC5uYW1lcyA9IGMoIiMgYXVkaW9ncmFtcyIsICIjIHN1YmplY3RzIikpICU+JQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSAic3RyaXBlZCIsIGZ1bGxfd2lkdGggPSBGKQpgYGAKCk5vdyBtYWtlIGEgaGlzdG9ncmFtIG9mIHRoZSBudW1iZXIgb2YgYXVkaW9ncmFtcyBhY3Jvc3MgcGF0aWVudHMgaW4gZWFjaCBvZiB0aGUgZ3JvdXBzCmBgYHtyfQojbnVtYmVyIChuKSBvZiBjb3VudHMgKGkuZS4gYXVkaW9ncmFtcykgcGVyIHN1YmplY3QgKHBpZCkKc3VtbWFyeXRhYmxlIDwtIGRhdGEgJT4lIGNvdW50KGdyb3VwLCBwaWQpCgpnZ3Bsb3QoZGF0YSA9IHN1bW1hcnl0YWJsZSwgYWVzKHggPSBuLCBmaWxsID0gZ3JvdXApKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAxKSArCiAgZmFjZXRfd3JhcCh+IGdyb3VwKSArCiAgeGxhYigiIyBvZiBtZWFzdXJlbWVudHMiKSArCiAgeWxhYigiRnJlcXVlbmN5ICgjIHBhdGllbnRzKSIpCmRldi5wcmludChwZGYsICcuLi9yZXN1bHRzL2hpc3RvZ3JhbV9udW1iZXJfbWVhc19waWQucGRmJykKYGBgCgpSZWxhdGlvbiBvZiBQVEEgd2l0aCBhZ2UgZm9yIHRoZSBkaWZmZXJlbnQgZ3JvdXBzOyBjb25uZWN0aW5nIGxpbmVzIHNob3cgbG9uZ2l0dWRpbmFsIGRhdGEgb2YgcGF0aWVudHMnIFBUQSBvdmVyIHRpbWUKCmBgYHtyfQpnZ3Bsb3QoZGF0YSwgYWVzKAogIHggPSBMZWVmdGlqZCwKICB5ID0gUFRBNTRBRFMsCiAgZ3JvdXAgPSBwaWQsCiAgY29sb3IgPSBncm91cAopKSArCiAgZ2VvbV9wb2ludChhZXMoY29sb3VyID0gZmFjdG9yKGdyb3VwKSkpICsKICBnZW9tX2xpbmUoZGF0YSA9IGRhdGEsIHNpemUgPSAxLCBhbHBoYSA9IC4zKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMCwgbGluZXR5cGUgPSAiZGFzaGVkIikgKwogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMCwgMTAwLCAyMCkpICsKICBzY2FsZV95X3JldmVyc2UoYnJlYWtzID0gc2VxKC0xMCwgMTMwLCAyMCksIGxpbWl0cyA9IGMoMTMwLCAtMTApKSArCiAgeGxhYigiQWdlICh5ZWFycykiKSArCiAgeWxhYigiUFRBIChkQiBITCkiKSArCiAgdGhlbWVfbGlnaHQoKQpkZXYucHJpbnQocGRmLCAnLi4vcmVzdWx0cy9wdGFfYWdlX3BpZF9ncm91cHMucGRmJykKYGBgCiMjIExvZ2lzdGljIGZpdCBvZiBQVEEgd2l0aCBhZ2UKUGVyZm9ybSBmaXRzIHRvIHRoZSBkYXRhOyBmaXJzdCBmb2N1cyBvbiBMQ0NMIGRvbWFpbi4gCgpgYGB7cn0KbGNjbCA9IHN1YnNldChkYXRhLCBncm91cCA9PSAiTENDTCIpCmdncGxvdChsY2NsLAogICAgICAgYWVzKHggPSBMZWVmdGlqZCwgeSA9IFBUQTU0QURTKSwKICAgICAgIGdyb3VwID0gcGlkLAogICAgICAgY29sb3IgPSBncm91cCkgKwogIHhsYWIoIkFnZSAoeWVhcnMpIikgKwogIHlsYWIoIlBUQSAoZEIgSEwpIikgKwogIGdlb21fcG9pbnQoYWVzKGNvbG91ciA9IGZhY3Rvcihncm91cCkpKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMCwgbGluZXR5cGUgPSAiZGFzaGVkIikgKwogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMCwgMTAwLCAyMCkpICsKICBzY2FsZV95X3JldmVyc2UoYnJlYWtzID0gc2VxKC0xMCwgMTMwLCAyMCksIGxpbWl0cyA9IGMoMTMwLC0xMCkpICsKICB0aGVtZV9saWdodCgpCmRldi5wcmludChwZGYsICcuLi9yZXN1bHRzL3B0YV9hZ2VfcGlkX2xjY2wucGRmJykKYGBgClRyeSB0byBmaXQgdGhlIGRhdGEgd2l0aCBhIGxpbmVhciBmdW5jdGlvbiwgYSBwb3dlci1sYXcgZnVuY3Rpb24gYW5kIGEgbG9naXN0aWMgZnVuY3Rpb246CmBgYHtyfQpsaW5fZml0IDwtCiAgbmxzKFBUQTU0QURTIH4gYSAqIExlZWZ0aWpkICsgYiwKICAgICAgZGF0YSA9IGxjY2wsCiAgICAgIHN0YXJ0ID0gbGlzdChhID0gMS41LCBiID0gMCkpCnN1bW1hcnkobGluX2ZpdCkKYGBgCmBgYHtyfQpubHNfZml0IDwtCiAgbmxzKFBUQTU0QURTIH4gYSAqIExlZWZ0aWpkIF4gYiwKICAgICAgZGF0YSA9IGxjY2wsCiAgICAgIHN0YXJ0ID0gbGlzdChhID0gMC4wNSwgYiA9IDEuNSkpCnN1bW1hcnkobmxzX2ZpdCkKYGBgCmBgYHtyfQpzdGFydHZlYyA8LSBjKEFzeW0gPSAxMjAsIHhtaWQgPSA1MCwgc2NhbCA9IDE1KQpubHNfbG9naXMgPC0gbmxzKFBUQTU0QURTIH4gU1Nsb2dpcyhMZWVmdGlqZCwgQXN5bSwgeG1pZCwgc2NhbCksCiAgICAgICAgICAgICAgICAgZGF0YSA9IGxjY2wsCiAgICAgICAgICAgICAgICAgc3RhcnQgPSBzdGFydHZlYykKc3VtbWFyeShubHNfbG9naXMpCmBgYAoKQ29tcGFyZSBwb3dlci1sYXcgZml0IGFuZCB0aGUgbG9naXN0aWMgZnVuY3Rpb25lIGFuZCBkaXNwbGF5IHRoZSByZXN1bHRzCmBgYHtyfQphbm92YShubHNfZml0LCBubHNfbG9naXMpCmBgYAoKCmBgYHtyfQpuZXdkYXQgPSBleHBhbmQuZ3JpZChMZWVmdGlqZCA9IHNlcSgwLCAxMDAsIGJ5ID0gMSkpCm5ld2RhdCRsaW4gPC0gcHJlZGljdChsaW5fZml0LCBuZXdkYXRhID0gbmV3ZGF0KQpuZXdkYXQkcHRhX2xvZ2lzdGljIDwtIHByZWRpY3QobmxzX2xvZ2lzLCBuZXdkYXRhID0gbmV3ZGF0KQpuZXdkYXQkcHRhX3Bvd2VyIDwtIHByZWRpY3QobmxzX2ZpdCwgbmV3ZGF0YSA9IG5ld2RhdCkKI25ld2RhdApnZ3Bsb3QobGNjbCwgYWVzKHggPSBMZWVmdGlqZCwgeSA9IFBUQTU0QURTKSkgKwogIGdlb21fcG9pbnQoYWVzKGNvbG91ciA9IGZhY3Rvcihncm91cCkpKSArCiAgZ2VvbV9saW5lKGRhdGEgPSBuZXdkYXQsCiAgICAgICAgICAgIGFlcyh5ID0gbGluKSwKICAgICAgICAgICAgc2l6ZSA9IDEsCiAgICAgICAgICAgIGNvbCA9ICdibHVlJykgKwogIGdlb21fbGluZShkYXRhID0gbmV3ZGF0LCBhZXMoeSA9IHB0YV9sb2dpc3RpYyksIHNpemUgPSAxKSArCiAgZ2VvbV9saW5lKGRhdGEgPSBuZXdkYXQsCiAgICAgICAgICAgIGFlcyh5ID0gcHRhX3Bvd2VyKSwKICAgICAgICAgICAgc2l6ZSA9IDEsCiAgICAgICAgICAgIGNvbCA9ICdyZWQnKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMCwgbGluZXR5cGUgPSAiZGFzaGVkIikgKwogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMCwgMTAwLCAyMCkpICsKICBzY2FsZV95X3JldmVyc2UoYnJlYWtzID0gc2VxKC0xMCwgMTMwLCAyMCksIGxpbWl0cyA9IGMoMTMwLCAtMTApKSArCiAgI3NjYWxlX3lfcmV2ZXJzZShsaW1pdHM9YygxMzAsLTEwKSkgKwogIHhsYWIoIkFnZSAoeWVhcnMpIikgKwogIHlsYWIoIlBUQSAoZEIgSEwpIikgKwogIHRoZW1lX2xpZ2h0KCkKZGV2LnByaW50KHBkZiwgJy4uL3Jlc3VsdHMvcHRhX2FnZV9sY2NsX2ZpdHMucGRmJykKCmBgYAogICAgICAgICAgICAKQXMgd2UgY2FuIHNlZSwgdGhlIGxvZ2lzdGljIGZ1bmN0aW9uIChTU2xvZ2lzKSBkZXNjcmliZXMgdGhlIGRhdGEgYmV0dGVyIHRoYW4gdGhlIHBvd2VyLWxhdyBmdW5jdGlvbiAoRiA9IDE4LDk7IHAgPSAxLjYgZS01KQpUaGlzIGZ1bmN0aW9uIGhhcyBhbHNvIGJlZW4gdXNlZCBpbiBkZXNyaWJpbmcgdGhlIChmcmVxdWVuY3ktc3BlY2lmaWMpIHRocmVzaG9sZHMgaW4gUGF1dyBldCBhbC4sIDIwMTEgYW5kIHdpbGwgdXNlZCBpbiB0aGUgc3Vic2VxdWVudCBzZWN0aW9ucy4KCiMjIEdyb3VwIGNvbXBhcmlzb24KVGhlIG1haW4gcXVlc3Rpb25zIGlzIHdoZXRoZXIgdGhlIGZ1bmN0aW9uIHRoYXQgZGVzY3JpYmVzIHRoZSBQVEEgKGRCIEhMKSBhcyBhIGZ1bmN0aW9uIG9mIGFnZSAoeWVhcnMpIGRpZmZlcnMgYmV0d2VlbiB0aGUgZ3JvdXBzIFxAcmVmKGZpZzpwbG90X3B0YV9hZ2VfZ3JvdXBzKS4gCgpTdGFydCB3aXRoIGEgZ3JvdXAtZml0OyBkaXNjYXJkaW5nIGdyb3VwaW5nIGluZm9ybWF0aW9uCmBgYHtyfQpmaXQwIDwtCiAgbmxzKFBUQTU0QURTIH4gU1Nsb2dpcyhMZWVmdGlqZCwgQXN5bSwgeG1pZCwgc2NhbCksIGRhdGEgPSBkYXRhKQpzdW1tYXJ5KGZpdDApCmNvZWYoZml0MCkKYGBgCk5vdywgYWRkIGEgZ3JvdXBpbmctdmFyaWFibGUgd2l0aCB0aGUgbWlkLXBvaW50ICh4bWlkKQoKYGBge3J9CiMgaHR0cHM6Ly9zdGF0cy5zdGFja2V4Y2hhbmdlLmNvbS9xdWVzdGlvbnMvMjcyNzMvaG93LWRvLWktZml0LWEtbm9ubGluZWFyLW1peGVkLWVmZmVjdHMtbW9kZWwtZm9yLXJlcGVhdGVkLW1lYXN1cmVzLWRhdGEtdXNpbmctbmwKIyBodHRwczovL3N0YXRzLnN0YWNrZXhjaGFuZ2UuY29tL3F1ZXN0aW9ucy8zMTY4MDEvaG93LXRvLWNvbXBhcmUtbG9naXN0aWMtcmVncmVzc2lvbi1jdXJ2ZXMKZml0MSA8LSBubHMoCiAgUFRBNTRBRFMgfiBTU2xvZ2lzKExlZWZ0aWpkLCBBc3ltLCB4bWlkW2dyb3VwXSwgc2NhbCksCiAgZGF0YSA9IGRhdGEsCiAgc3RhcnQgPSBsaXN0KAogICAgQXN5bSA9IHJlcCgxMjAsIDEpLAogICAgeG1pZCA9IHJlcCg1MCwgMyksCiAgICBzY2FsID0gcmVwKDE1LCAxKQogICkKKQpzdW1tYXJ5KGZpdDEpCmBgYApBbmQgYWRkIHRoZSBzY2FsaW5nIFtzY2FsXSBhcyBhIGdyb3VwaW5nIHZhcmlhYmxlOyBkb2VzIGl0IGZ1dGhlciBleHBsYWluIGRpZmZlcmVuY2VzIGJldHdlZW4gZ3JvdXBzPwoKYGBge3J9CmZpdDIgPC0KICBubHMoCiAgICBQVEE1NEFEUyB+IFNTbG9naXMoTGVlZnRpamQsIEFzeW0sIHhtaWRbZ3JvdXBdLCBzY2FsW2dyb3VwXSksCiAgICBkYXRhID0gZGF0YSwKICAgIHN0YXJ0ID0gbGlzdCgKICAgICAgQXN5bSA9IHJlcCgxMjAsIDEpLAogICAgICB4bWlkID0gcmVwKDUwLCAzKSwKICAgICAgc2NhbCA9IHJlcCgxNSwgMykKICAgICkKICApCnN1bW1hcnkoZml0MikKYGBgCmBgYHtyfQpwbG90KHByb2ZpbGUoZml0MikpCmBgYAoKQW5kIG5vdyBhZGQgdGhlIGFzeW1wdG90aWMgdmFsdWUgb2YgdGhlIGZpdCAoTGVlZnRpamQgLT4gaW5maW5pdHkpIChBc3ltKToKYGBge3J9CmZpdDMgPC0KICBubHMoCiAgICBQVEE1NEFEUyB+IFNTbG9naXMoTGVlZnRpamQsIEFzeW1bZ3JvdXBdLCB4bWlkW2dyb3VwXSwgc2NhbFtncm91cF0pLAogICAgZGF0YSA9IGRhdGEsCiAgICBzdGFydCA9IGxpc3QoCiAgICAgIEFzeW0gPSByZXAoMTIwLCAzKSwKICAgICAgeG1pZCA9IHJlcCg1MCwgMyksCiAgICAgIHNjYWwgPSByZXAoMTUsIDMpCiAgICApCiAgKQpzdW1tYXJ5KGZpdDMpCmBgYAoKTm93IHRlc3QgdGhlIHZhcmlvdXMgbW9kZWxzLiBXaGljaCBvZiB0aGUgcGFyYW1ldGVycyBleHBsYWluIHRoZSBkYXRhIGJlc3Q/CmBgYHtyfQphbm92YShmaXQwLCBmaXQxLCBmaXQyLCBmaXQzKQpgYGAKSXQgdHVybnMgb3V0IHRoZSBib3RoIHRoZSB2YXJpYWJsZXMgW3htaWRdIGFuZCBbc2NhbGVdLCBpLmUuIHRoZSBtaWRwb2ludCBhbmQgc2xvcGUgYXQgdGhlIG1pZHBvaW50IHNpZ25pZmljYW50bHkgZGlmZmVyIGJldHdlZW4gdGhlIHRocmVlIGdyb3VwcywgYnV0IHRoYXQgYWRkaW5nIHRoZSBhc3ltcHRvdGljIHZhbHVlIGRvZXMgbm90IGRlc2NyaWJlIHRoZSBkYXRhIHNpZ25pZ2ljYW50bHkgYmV0dGVyIChGPTAuODksIHA9MC40MSkuIEZpdCB0aGUgZGF0YSBhbmQgcGxvdCB0aGUgcmVzdWx0czoKCgpgYGB7cn0KbmV3ZGF0ID0gZXhwYW5kLmdyaWQoTGVlZnRpamQgPSBzZXEoMCwgMTAwLCBieSA9IDEpLAogICAgICAgICAgICAgICAgICAgICBncm91cCA9IGMoIkxDQ0wiLCAidldGQTEiLCAidldGQTIiKSkKbmV3ZGF0JGZpdCA8LSBwcmVkaWN0KGZpdDIsIG5ld2RhdGEgPSBuZXdkYXQpCmBgYApgYGB7cn0KZ2dwbG90KGRhdGEsIGFlcygKICB4ID0gTGVlZnRpamQsCiAgeSA9IFBUQTU0QURTLAogIGdyb3VwID0gcGlkLAogIGNvbG9yID0gZ3JvdXAKKSkgKwogIGdlb21fcG9pbnQoYWVzKGNvbG91ciA9IGZhY3Rvcihncm91cCkpLCBhbHBoYSA9IC40KSArCiAgZ2VvbV9saW5lKGRhdGEgPSBkYXRhLCBzaXplID0gMSwgYWxwaGEgPSAuMikgKwogIGdlb21fbGluZShkYXRhID0gbmV3ZGF0LAogICAgICAgICAgICBhZXMoCiAgICAgICAgICAgICAgeSA9IGZpdCwKICAgICAgICAgICAgICBncm91cCA9IGdyb3VwLAogICAgICAgICAgICAgIGNvbG91ciA9IGZhY3Rvcihncm91cCkKICAgICAgICAgICAgKSwKICAgICAgICAgICAgc2l6ZSA9IDEpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAwLCBsaW5ldHlwZSA9ICJkYXNoZWQiKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLCAxMDAsIDIwKSkgKwogIHNjYWxlX3lfcmV2ZXJzZShicmVha3MgPSBzZXEoLTEwLCAxMzAsIDIwKSwgbGltaXRzID0gYygxMzAsIC0xMCkpICsKICB4bGFiKCJBZ2UgKHllYXJzKSIpICsKICB5bGFiKCJQVEEgKGRCIEhMKSIpICsKICB0aGVtZV9saWdodCgpCmRldi5wcmludChwZGYsICcuLi9yZXN1bHRzL3B0YV9hZ2VfcGlkX2dyb3Vwc19maXRzLnBkZicpCmBgYAoKTm93IHBlcmZvcm0gZml0IG9uIGluZGl2aWR1YWwgZGF0YSBieSBzdWJzZXR0aW5nIHRoZSBkYXRhIHRvIGtlZXAgaW5kaXZpZHVhbHMgd2l0aCBtb3JlIHRoYXQgeD0yIGxvbmdpdHVkaW5hbCBkYXRhcG9pbnRzLiBJcyBpdCB0aGUgY2FzZSB0aGF0IHVzaW5nIGEgbm9uLWxpbmVhciBtaXhlZC1tb2RlbCBhcHByb2FjaCBtYXkgaGVscCB1cz8KCmBgYHtyfQojaHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvMTQ0Mzk3NzAvZmlsdGVyLXJvd3MtaW4tZGF0YWZyYW1lLWJ5LW51bWJlci1vZi1yb3dzLXBlci1sZXZlbC1vZi1hLWZhY3RvcgpwaWRsZW5ndGhzIDwtIGF2ZShhcy5udW1lcmljKGRhdGEkcGlkKSwKICAgICAgICAgICAgICAgICAgZGF0YSRwaWQsIEZVTiA9IGxlbmd0aCkKI2RmMiA8LSBsY2NsW3BpZGxlbmd0aHMgPiA1LCBdCmRmMiA8LSBkYXRhW3BpZGxlbmd0aHMgPiAyLF0KdDMgPC0gd2l0aChkZjIsIHRhYmxlKGdyb3VwKSkKCmBgYApTbywgd2l0aCBvbmx5IHR3byBkYXRhLXBvaW50cywgb25seSAzIGFuZCA2IHN1YmplY3RzIGZvciB0aGUgdldGQTEgYW5kIHZXRkEyIGRvbWFpbiByZXNwZWN0aXZlbHksIHJlbWFpbi4gTm93LCBmaXQgdGhvc2Ugd2l0aCBhIGxvZ2lzdGljIGZ1bmN0aW9uIChTU2xvZ2lzKSB1c2luZyBubHNsaXN0LgoKYGBge3J9Cm1vZGVscyA8LQogIG5sc0xpc3QoUFRBNTRBRFMgfiBTU2xvZ2lzKExlZWZ0aWpkLCBBc3ltLCB4bWlkLCBzY2FsKSB8CiAgICAgICAgICAgIHBpZCwgZGF0YSA9IGRmMikKYGBgCkFzIHdlIGNhbiBzZWUsIHNvbWUgbW9kZWwtcHJlZGljdGlvbnMgZmFpbGVkOyB0aGV5IGVuZCB1cCB3aXRoIE5hTnMgaW4gdGhlIG1vZGVsIGZpdCBsaXN0IChubHNsaXN0KTsgc2VlIGUuZy4gcGlkIDE0NwoKYGBge3J9CmRhdGFfaWQgPC0gc3Vic2V0KGRmMiwgcGlkID09ICIxNDciKQpkYXRhX2lkCmdncGxvdChkYXRhID0gZGF0YV9pZCwgIGFlcyh4ID0gTGVlZnRpamQsIHkgPSBQVEE1NEFEUykpICsKICBnZW9tX3BvaW50KCkgKwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDAsIGxpbmV0eXBlID0gImRhc2hlZCIpICsKICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsIDEwMCwgMjApKSArCiAgc2NhbGVfeV9yZXZlcnNlKGJyZWFrcyA9IHNlcSgtMTAsIDEzMCwgMjApLCBsaW1pdHMgPSBjKDEzMCwgLTEwKSkgKwogICNzY2FsZV95X3JldmVyc2UobGltaXRzPWMoMTMwLC0xMCkpICsKICB4bGFiKCJBZ2UgKHllYXJzKSIpICsKICB5bGFiKCJQVEEgKGRCIEhMKSIpICsKICB4bGltKDAsMTAwKSsKICB0aGVtZV9saWdodCgpIApgYGAKClByZWRpY3QgZm9yIGFsbCBwaWQncyB0aGUgZml0IHRvIHRoZSBtb2RlbCBhbmQgcmVtb3ZlIHRoZSBwaWQncyB0aGF0IGdpdmUgTmFOcy4gQ2hlY2sgaG93IG1hbnkgc3ViamVjdHMgcGVyIGdyb3VwIHdlIGVuZCB1cCB3aXRoLgoKYGBge3J9CmRmMiRQcmVkIDwtIHByZWRpY3QobW9kZWxzKQpkZjJfbmEgPC0gbmEub21pdChkZjIpCmRmMl9uYV9zdGF0cyA8LSB3aXRoKGRmMl9uYSwgdGFibGUoZ3JvdXAsIHBpZCkpCmRmMl9uYV9zdGF0cwpgYGAKV2Ugb25seSBrZWVwIHRoZSBwaWQncyBmcm9tIHRoZSBMQ0NMIGdyb3VwLiBUaGUgcGlkJ3MgaW4gdGhlIG90aGVyIGdyb3VwcyBhcmUgbm90IHByb3Blcmx5IGZpdHRlZC4gQWxzbyBub3RlIHRoYXQgdGhlIG1pbmltdW0gb2YgZGF0YS1wb2ludHMgZm9yIGEgcmVhc29uYWJsZSBmaXQgaXMgNC4KCgpgYGB7cn0KbGUgPC0gdW5pcXVlKGRmMl9uYSRwaWQpCm5ld2RhdCA9IGV4cGFuZC5ncmlkKExlZWZ0aWpkID0gc2VxKDAsIDEwMCwgYnkgPSAxKSwgcGlkID0gbGUpCm5ld2RhdCRwcmVkbmxtIDwtIHByZWRpY3QobW9kZWxzLCBuZXdkYXRhID0gbmV3ZGF0KQpgYGAKCmBgYHtyIGZpZy5oZWlnaHQgPSA1LCBmaWcud2lkdGggPSA3fQojaHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvMzcxMjI5OTQvcGxvdHRpbmctYS1saXN0LW9mLW5vbi1saW5lYXItcmVncmVzc2lvbnMtd2l0aC1nZ3Bsb3QKI2h0dHBzOi8vYW9zbWl0aC5yYmluZC5pby8yMDE4LzExLzE2L3Bsb3QtZml0dGVkLWxpbmVzLwpnZ3Bsb3QoZGF0YSA9IGRmMl9uYSwgIGFlcyh4ID0gTGVlZnRpamQsIHkgPSBQVEE1NEFEUywgY29sb3VyID0gcGlkKSkgKwogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV9saW5lKGRhdGEgPSBuZXdkYXQsIGFlcyh5ID0gcHJlZG5sbSkpICsKICBmYWNldF93cmFwKH4gcGlkKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMCwgbGluZXR5cGUgPSAiZGFzaGVkIikgKwogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMCwgMTAwLCAyNSkpICsKICBzY2FsZV95X3JldmVyc2UoYnJlYWtzID0gc2VxKDAsIDEyMCwgNDApLCBsaW1pdHMgPSBjKDEzMCwtMTApKSArCiAgI3NjYWxlX3lfcmV2ZXJzZShsaW1pdHM9YygxMzAsLTEwKSkgKwogIHhsYWIoIkFnZSAoeWVhcnMpIikgKwogIHlsYWIoIlBUQSAoZEIgSEwpIikgKwogIHRoZW1lX2xpZ2h0KCkKCmRldi5wcmludChwZGYsICcuLi9yZXN1bHRzL3B0YV9hZ2VfcGlkX2xjY2xfaW5kX2ZpdHMucGRmJykKYGBgCgpTbywgaXQgc2VlZW1zIHdlIGNhbiBmaXQgdGhlIGRhdGEgZm9yIGluZGl2aWR1YWwgc3ViamVjdHMgYnkgc29tZSBleHRlbnQuIEl0IG9mdGVuICdmYWlscycgYnkgb3Zlci0gb3IgdW5kZXJlc3RpbWF0aW5nIHRoZSB0YWlsIChjb2VmLmxtbGlzdCBBc3ltIGNvbHVtbikuIFdlIGNhbiBhbHNvIHBsb3QgaXQgYWxsIGluIG9uZSBmaWd1cmUuCgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBkZjJfbmEsICBhZXMoeCA9IExlZWZ0aWpkLCB5ID0gUFRBNTRBRFMsIGNvbG91ciA9IHBpZCkpICsKICBnZW9tX3BvaW50KCkgKwogIGdlb21fbGluZShkYXRhID0gbmV3ZGF0LCBhZXMoeSA9IHByZWRubG0sIGdyb3VwID0gcGlkKSkgKwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDAsIGxpbmV0eXBlID0gImRhc2hlZCIpICsKICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsIDEwMCwgMjApKSArCiAgc2NhbGVfeV9yZXZlcnNlKGJyZWFrcyA9IHNlcSgtMTAsIDEzMCwgMjApLCBsaW1pdHMgPSBjKDEzMCwtMTApKSArCiAgeGxhYigiQWdlICh5ZWFycykiKSArCiAgeWxhYigiUFRBIChkQiBITCkiKSArCiAgdGhlbWVfbGlnaHQoKQpkZXYucHJpbnQocGRmLCAnLi4vcmVzdWx0cy9wdGFfYWdlX3BpZF9sY2NsX2luZF9maXRzX292ZXJsYXkucGRmJykKYGBgCkZlZWQgdGhlIHJlbWFpbmluZyBkYXRhIGludG8gdGhlIG5vbi1saW5lYXIgbWl4ZWQtbW9kZWxzIHdpdGggdGhlIHBhcmFtZXRlcnMgQXN5bSwgeG1pZCwgYW5kIHNjYWwgYXMgcmFuZG9tIGZhY3RvcnMuCgpgYGB7cn0Kbm0xIDwtCiAgbmxtZXIoCiAgICBQVEE1NEFEUyB+IFNTbG9naXMoTGVlZnRpamQsIEFzeW0sIHhtaWQsIHNjYWwpIH4gQXN5bSArIHhtaWQgKyBzY2FsIHwKICAgICAgcGlkLAogICAgZGYyX25hLAogICAgbWV0aG9kPSJNTCIsCiAgICBzdGFydCA9IGMoQXN5bSA9IDEwMCwgeG1pZCA9IDYwLCBzY2FsID0gMTUpLAogICAgY29yciA9IEZBTFNFCiAgKQpzdW1tYXJ5KG5tMSkKYGBgCmBgYHtyfQpwbG90KHJhbmVmKG5tMSxhdWdGcmFtZT1UKSkKYGBgCgpgYGB7cn0KcGFyYW1zIDwtIGNvZWYobm0xKQpoZWFkKHBhcmFtcykKI3NqcGxvdChubTEpCmBgYAoKYGBge3J9CnJlcXVpcmUobGF0dGljZSkKcXFtYXRoKHJhbmVmKG5tMSwgY29uZFZhciA9IFRSVUUpKQpgYGAKCiMjIEZyZXF1ZW5jZXktc3BlY2lmaWMgYW5hbHlzZXMKTm93IHN1YnNldCB0aGUgZGF0YSB0byBjb250YWluIGluZGl2aWR1YWwgZnJlcXVlbmNpZXMKYGBge3J9CmRhdGFfYWxsIDwtCiAgc3Vic2V0KAogICAgZGF0YV9zdWJzZXQsCiAgICBzZWxlY3QgPSBjKAogICAgICAncGlkJywKICAgICAgJ2dyb3VwJywKICAgICAgJ0xlZWZ0aWpkJywKICAgICAgJzI1MC5BRCcsCiAgICAgICc1MDAuQUQnLAogICAgICAnMTAwMC5BRCcsCiAgICAgICcyMDAwLkFEJywKICAgICAgJzQwMDAuQUQnLAogICAgICAnODAwMC5BRCcsCiAgICAgICcyNTAuQVMnLAogICAgICAnNTAwLkFTJywKICAgICAgJzEwMDAuQVMnLAogICAgICAnMjAwMC5BUycsCiAgICAgICc0MDAwLkFTJywKICAgICAgJzgwMDAuQVMnCiAgICApCiAgKQpoZWFkKGRhdGFfYWxsKQpgYGAKQ29udmVydCAnd2lkZScgZGF0YXNldCBpbnRvICdsb25nJyBmb3JtYXQgdXNpbmcgdGlkeXIgYW5kIHJlbW92ZSBOYU5zCgpgYGB7cn0KdGlkaWVyIDwtIGRhdGFfYWxsICU+JQogIGdhdGhlcihmLCBkQiwtcGlkLC1ncm91cCwtTGVlZnRpamQpCmRhdGFfYWxsX2wgPC0gdGlkaWVyICU+JQogIHNlcGFyYXRlKGYsIGludG8gPSBjKCJmcmVxdWVuY3kiLCAiZWFyIiksIHNlcCA9ICJcXC4iKQojaGVhZChkYXRhX2FsbF9sKQpkYXRhX2FsbF9sJGZyZXF1ZW5jeSA9IGZhY3RvcihkYXRhX2FsbF9sJGZyZXF1ZW5jeSkKZGF0YV9hbGxfbCRlYXIgPSBmYWN0b3IoZGF0YV9hbGxfbCRlYXIpCmRhdGFfYWxsX2wgPC0gbmEub21pdChkYXRhX2FsbF9sKQpoZWFkKGRhdGFfYWxsX2wpCmBgYAoKCmBgYHtyfQpwIDwtIGRhdGFfYWxsX2wgJT4lCiAgbXV0YXRlKGZyZXF1ZW5jeSA9IGZjdF9yZWxldmVsKGZyZXF1ZW5jeSwgIjI1MCIsICI1MDAiLCAiMTAwMCIsICIyMDAwIiwgIjQwMDAiLCAiODAwMCIpKSAlPiUKICBnZ3Bsb3QoYWVzKHggPSBmcmVxdWVuY3ksIHkgPSBkQikpICsKICAjZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiKSArCiAgI2dlb21faGlzdG9ncmFtKCkgKwogIGdlb21fdmlvbGluKCkgKwogIGZhY2V0X3dyYXAoIH4gZ3JvdXAsIG5jb2wgPSAzKSArCiAgI2dlb21fcG9pbnQoKQogIHhsYWIoIkZyZXF1ZW5jeSAoSHopIikgKwogIHlsYWIoIkhlYXJpbmcgbGV2ZWwgKGRCKSIpICsKICBzY2FsZV95X3JldmVyc2UoYnJlYWtzID0gc2VxKC0xMCwgMTMwLCAyMCksIGxpbWl0cyA9IGMoMTMwLCAtMTApKSArCiAgdGhlbWVfbGlnaHQoKQogICN0aGVtZV9jbGFzc2ljKCkKcApkZXYucHJpbnQocGRmLCAnLi4vcmVzdWx0cy92aW9saW5fcGxvdF9ITF9ncm91cHMucGRmJykKYGBgCmBgYHtyIGZpZy5oZWlnaHQgPSA0LCBmaWcud2lkdGggPSA2fQpkYXRhX2FsbF9sJGYgPSBmYWN0b3IoZGF0YV9hbGxfbCRmcmVxdWVuY3ksCiAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjKCcyNTAnLCAnNTAwJywgJzEwMDAnLCAnMjAwMCcsICc0MDAwJywgJzgwMDAnKSkKZ2dwbG90KGRhdGFfYWxsX2wsIGFlcygKICB4ID0gTGVlZnRpamQsCiAgeSA9IGRCLAogIGdyb3VwID0gZiwKICBjb2xvciA9IGdyb3VwCikpICsKICBnZW9tX3BvaW50KHNpemUgPSAwLjcpICsKICBmYWNldF93cmFwKCB+IGYpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAwLCBsaW5ldHlwZSA9ICJkYXNoZWQiKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLCAxMDAsIDI1KSkgKwogIHNjYWxlX3lfcmV2ZXJzZShicmVha3MgPSBzZXEoMCwgMTIwLCA0MCksIGxpbWl0cyA9IGMoMTMwLCAtMTApKSArCiAgI3NjYWxlX3lfcmV2ZXJzZShsaW1pdHM9YygxMzAsLTEwKSkgKwogIHhsYWIoIkFnZSAoeWVhcnMpIikgKwogIHlsYWIoIkhlYXJpbmcgbGV2ZWwgKGRCIEhMKSIpICsKICB0aGVtZV9saWdodCgpCmRldi5wcmludChwZGYsICcuLi9yZXN1bHRzL0hMX2FnZV9mcmVxdWVuY3lfZ3JvdXBzLnBkZicpCgpgYGAKCgpgYGB7cn0KbW9kZWxzIDwtCiAgbmxzTGlzdCgKICAgIGRCIH4gU1Nsb2dpcyhMZWVmdGlqZCwgQXN5bSwgeG1pZCwgc2NhbCkgfAogICAgICBmLAogICAgZGF0YSA9IGRhdGFfYWxsX2wsCiAgICBzdGFydCA9IGMoQXN5bSA9IDEwMCwgeG1pZCA9IDYwLCBzY2FsID0gMTUpCiAgKQpzdW1tYXJ5KG1vZGVscykKCmBgYAoKYGBge3IgZmlnLmhlaWdodCA9IDcsIGZpZy53aWR0aCA9IDEwfQpnZ3Bsb3QoZGF0YSA9IGRhdGFfYWxsX2wsIGFlcygKICB4ID0gTGVlZnRpamQsCiAgeSA9IGRCLAogIGdyb3VwID0gZ3JvdXAsCiAgY29sb3IgPSBncm91cAopKSArCiAgI2dlb21fcG9pbnQoc2l6ZT0wLjQpICsKICBnZW9tX2ppdHRlcihzaXplID0gMC40KSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIikgKwogIGZhY2V0X3dyYXAoIH4gZikgKwogIHhsYWIoIkFnZSAoeWVhcnMpIikgKwogIHlsYWIoIlBUQSAoZEIgSEwpIikgKwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDAsIGxpbmV0eXBlID0gImRhc2hlZCIpICsKICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsIDEwMCwgMjApKSArCiAgc2NhbGVfeV9yZXZlcnNlKGJyZWFrcyA9IHNlcSgtMTAsIDEzMCwgMjApLCBsaW1pdHMgPSBjKDEzMCwgLTEwKSkgKwogIHRoZW1lX2xpZ2h0KCkKCmBgYApOb3cgdXNlIHRoZSBsaW5lYXIgZml0cyB0byBjb25zdHJ1Y3QgYW4gQVJUQSAKYGBge3IsIGZpZy5oZWlnaHQgPSA1LCBmaWcud2lkdGggPSA1LCBmaWcuYXNwPTAuOH0KcGFyKHB0eSA9ICJzIikKI3NlZTogaHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvMTE2OTUzOS9saW5lYXItcmVncmVzc2lvbi1hbmQtZ3JvdXAtYnktaW4tcgpsaWJyYXJ5KGxtZTQpCmFydGFfZGF0YSA8LSBzdWJzZXQoZGF0YV9hbGxfbCwgZ3JvdXAgPT0gInZXRkEyIikKZml0cy5wbG0gPC0gbG1MaXN0KGRCIH4gTGVlZnRpamQgfCBmcmVxdWVuY3ksIGRhdGEgPSBhcnRhX2RhdGEpCmNvZWYoZml0cy5wbG0pCmNpIDwtIGNvbmZpbnQoZml0cy5wbG0pCnBsb3QoY2kpCm5ld2RhdCA9IGV4cGFuZC5ncmlkKAogIExlZWZ0aWpkID0gc2VxKDIwLCA3MCwgYnkgPSAxMCksCiAgZnJlcXVlbmN5ID0gYygiMjUwIiwgIjUwMCIsICIxMDAwIiwgIjIwMDAiLCAiNDAwMCIsICI4MDAwIikKKQpuZXdkYXQkZml0IDwtIHByZWRpY3QoZml0cy5wbG0sIG5ld2RhdGEgPSBuZXdkYXQpCgpoZWFkKG5ld2RhdCkKCmdncGxvdChkYXRhID0gbmV3ZGF0LCBhZXMoeCA9IGZyZXF1ZW5jeSwgeSA9IGZpdCwgZ3JvdXAgPSBMZWVmdGlqZCkpICsKICBnZW9tX2xpbmUoYWVzKAogICAgeCA9IGZyZXF1ZW5jeSwKICAgIHkgPSBmaXQsCiAgICBncm91cCA9IExlZWZ0aWpkLAogICAgY29sb3IgPSBMZWVmdGlqZAogICkpICsKICBzY2FsZV95X3JldmVyc2UoYnJlYWtzID0gc2VxKC0xMCwgMTMwLCAyMCksIGxpbWl0cyA9IGMoMTMwLCAtMTApKSArCiAgIyBzY2FsZV94X2Rpc2NyZXRlKGJyZWFrcz1jKCIyNTAiLCI1MDAiLCIxMDAwIiwiMjAwMCIsIjQwMDAiLCI4MDAwIiksIGxhYmVscz1jKCIwLjI1IiwiMC41IiwiMSIsIjIiLCI0IiwiOCIiKSkgKwogIHhsYWIoIkZyZXF1ZW5jeSAoSHopIikgKwogIHlsYWIoIkhlYXJpbmcgdGhyZXNob2xkIChkQiBITCkiKSArCiAgZ3VpZGVzKGNvbG9yID0gZ3VpZGVfbGVnZW5kKCJMZWVmdGlqZCIpKSArCiAgdGhlbWVfY2xhc3NpYygpCmBgYAoKYGBge3J9CmxpYnJhcnkoZHBseXIpCmZpdHRlZF9tb2RlbHMgPSBkYXRhX2FsbF9sICU+JSBncm91cF9ieShmcmVxdWVuY3kpICU+JSBkbyhtb2RlbCA9IGxtKGRCIH4gTGVlZnRpamQsIGRhdGEgPSAuKSkKZml0dGVkX21vZGVscwpgYGAKCgpzaW1wbGUgbGluZWFyIG1vZGVsOiBQVEEgaXMgYSBmdW5jdGlvbiBvZiB0aGUgYWZmZWN0ZWQgZG9tYWluOyBoZXJlIGFyZSB0d28gbGV2ZWxzIGluIHRoaXMgbWl4ZWQgbW9kZWw7IAoxOiB0aW1lcG9pbnRzIGZvciBlYWNoIHBhdGllbnQ7IDI6IGdlbmV0aWMgZG9tYWluLCB3aXRoIGRvbWFpbiB0aGUgZml4ZWQgZWZmZWN0IGFuZCBwYXRpZW50IHRoZSByYW5kb20gZWZmZWN0IGFsbG93aW5nCnRoZSBpbnRlcmNlcHQgdG8gdmFyeSBhY3Jvc3MgcGF0aWVudCAofjF8cGlkKS4KCmBgYHtyfQpyYW5kb21faW50ZXJjZXB0IDwtIGxtZSgKICBkQiB+IGZyZXF1ZW5jeSAsCiAgcmFuZG9tID0gfiAxIHwgcGlkLAogICNwLiA4OTYKICBtZXRob2QgPSAiTUwiLAogIG5hLmFjdGlvbiA9IG5hLmV4Y2x1ZGUsCiAgY29udHJvbCA9IGxpc3Qob3B0ID0gIm9wdGltIiksCiAgY29ycmVsYXRpb24gPSBjb3JBUjEoKSwKICAjc2VlIHAuODk3OyB0aW1lcG9pbnRzIGFyZSBub3QgZXF1YWxseSBzcGFjZWQ7dXNlIGNvckNBUjEKICBkYXRhID0gZGF0YV9hbGxfbAopCnN1bW1hcnkocmFuZG9tX2ludGVyY2VwdCkKYW5vdmEocmFuZG9tX2ludGVyY2VwdCkKYGBgCm5vdyBhZGQgTGVlZnRpamQgYXMgZml4ZWQgZWZmZWN0OyBQVEEgfiBEb21haW4gKyBMZWVmdGlqZGAgKHNlZS4gZS5nLiBwLjg5NykKYGBge3J9CnRpbWVSSSA8LSB1cGRhdGUocmFuZG9tX2ludGVyY2VwdCwgLiB+IC4gKyBMZWVmdGlqZCkKc3VtbWFyeSh0aW1lUkkpCmBgYAoKYGBge3J9CnNlc3Npb25JbmZvKCkKYGBgCgojIENvZGUgQXBwZW5kaXgKYGBge3IgZ2V0bGFiZWxzLCBlY2hvID0gRkFMU0V9CmxhYnMgPSBrbml0cjo6YWxsX2xhYmVscygpCmxhYnMgPSBsYWJzWyFsYWJzICVpbiUgYygic2V0dXAiLCAidG9jIiwgImdldGxhYmVscyIsICJhbGxjb2RlIildCmBgYAoKYGBge3IgYWxsY29kZSwgcmVmLmxhYmVsID0gbGFicywgZXZhbCA9IEZBTFNFfQpgYGAKCg==